// Win32Project.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "Win32Project.h"
#include <gl/gl.h>
#include <cassert>
#include <cmath>
#include <crtdbg.h> // For OutputDebugString()
#include <sstream>
#pragma comment (lib, "opengl32.lib")

#define MAX_LOADSTRING 100

using namespace std;

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
HWND				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
HGLRC				InitializeOpenGL( HDC );
void				ShutdownOpenGL( HGLRC& );
void				SetupProjection( HWND );
void				RenderScene( HDC );

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_WIN32PROJECT, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	HWND hWnd = InitInstance (hInstance, nCmdShow);
	if (!hWnd)
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT));

	HDC hDC = GetDC( hWnd );

	HGLRC glRenderingContext = InitializeOpenGL( hDC );

	SetupProjection( hWnd );

	// Main loop:
	while (true)
	{
		// Process input, including Windows messages.
		//
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
		{
			if( msg.message == WM_QUIT )
			{
				// Application quitting.
				break;
			}

			if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else
		{
			RenderScene( hDC );
		}
	}

	// Shutdown
	//
	ShutdownOpenGL( glRenderingContext );
	ReleaseDC( hWnd, hDC );

	return (int) msg.wParam;
}

HGLRC InitializeOpenGL( HDC hdcWindow )
{
	assert( hdcWindow );

	// Set the window pixel format
	//
	PIXELFORMATDESCRIPTOR pixelFormatDescriptor = {0};

	pixelFormatDescriptor.nSize = sizeof( pixelFormatDescriptor );
	pixelFormatDescriptor.nVersion = 1;

	pixelFormatDescriptor.dwFlags = 
		PFD_DRAW_TO_WINDOW | 
		PFD_SUPPORT_OPENGL | 
		PFD_DOUBLEBUFFER;
	pixelFormatDescriptor.dwLayerMask = PFD_MAIN_PLANE;
	pixelFormatDescriptor.iPixelType = PFD_TYPE_RGBA;
	pixelFormatDescriptor.cColorBits = 32;
	pixelFormatDescriptor.cDepthBits = 0;	// No depth buffer for now.

	int pixelFormat = ChoosePixelFormat( hdcWindow, &pixelFormatDescriptor );
	assert (pixelFormat != 0);
	SetPixelFormat( hdcWindow, pixelFormat, &pixelFormatDescriptor );

	// Create the OpenGL render context
	//
	HGLRC renderingContext = wglCreateContext( hdcWindow );
	wglMakeCurrent ( hdcWindow, renderingContext );

	return renderingContext;
}

void ShutdownOpenGL( HGLRC& renderingContext )
{
	wglMakeCurrent( NULL, NULL );
	wglDeleteContext( renderingContext );
	renderingContext = 0;
}

void SetupProjection( HWND hWnd )
{
	assert( hWnd );

	// Determine the window size.
	//
	RECT clientRect;
	GetClientRect( hWnd, &clientRect );
	const float windowHalfWidth  = clientRect.right  * 0.5f;
	const float windowHalfHeight = clientRect.bottom * 0.5f;

	// Set the viewport.
	//
	glViewport( 0, 0, clientRect.right, clientRect.bottom );

	// Set the projection.
	//
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glOrtho( 
		-windowHalfWidth, 
		 windowHalfWidth, 
		 windowHalfHeight, 
		-windowHalfHeight, 
		 0, 1.0 );

	// Return to ModelView matrix.
	// All following matrix calls will target this matrix.
	glMatrixMode( GL_MODELVIEW );
}

void RenderScene( HDC hdcWindow )
{
	// Clear the draw buffer.
	//
	DWORD time = GetTickCount();

	glClearColor( 0.2f, 0.2f, 0.3f, 1.0f );
	glClear( GL_COLOR_BUFFER_BIT );

	wstringstream helper;
	helper << TEXT("the time is ") << time << endl;

	OutputDebugString(helper.str().c_str());

	// Set the ModelView matrix back to identity.
	//
	glLoadIdentity();

	// Render.
	//

	// Present the completed draw buffer.
	//
	SwapBuffers( hdcWindow );

}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return NULL;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return hWnd;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		// Parse the menu selections:
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}
